/*
 * Service.hpp
 *
 *  Created on: 2017年6月3日
 *      Author: Administrator
 */

#ifndef HPP_SERVICE_HPP_
#define HPP_SERVICE_HPP_

#include "../service.h"

#include <core/core.hpp>
#include <core/core_c.h>
#include <core/mat.hpp>
#include <core/operations.hpp>
#include <core/types_c.h>
#include <highgui/highgui.hpp>
#include <highgui/highgui_c.h>
#include <imgproc/imgproc.hpp>
#include <imgproc/types_c.h>
#include <winbase.h>
#include <winnt.h>
#include <algorithm>
#include <utility>

namespace service {

const float nantian = 0.7467329193;
// 内收几个像素
const int INN_PX = 18;
bool test;
void execute(const char* filepath, bool b, bool b1, int model);
void execute(Mat mat, bool multiple, bool testWin, int model);
bool check1(Rect rect, Point point);
// 寻找目标区域
Vector<Rect> find(Mat mat, bool b);
// 寻找最中心的矩形
Rect findCenterRect(Mat mat, Vector<Rect> rects);
// 寻找面积最大的矩形
Rect findMaxRect(Mat mat, Vector<Rect> rects);
// 缩小矩形
Rect suoxiaoRect(Mat mat, Rect r);
// 无用
bool check(Vec3b pex1, Vec3b pex2);
// 计算区域面积
int getRectArea(Rect rect);
// 根据长宽比例过滤
Vector<Rect> filterRectsSize(Vector<Rect> rects);
// 重合过滤
Vector<Rect> filterRects(Mat mat, Vector<Rect> rects);
// 简单过滤
void simpleFileter(Mat img, Vector<Format> &formats, Vector<Format>&tmps);
// 版面数据配置路径
void loadFormatData(const char* filepath);
void getRows(Mat shard, Vector<int> &rows);
void getCols(Mat shard, Vector<int>& cols);
Rect seekCut(Mat mat, Rect rect);
float cal(Mat mat1, Mat mat2);
//模板函数：将string类型变量转换为常用的数值类型（此方法具有普遍适用性）
template<class Type>
Type stringToNum(const string& str);
// 画框图像的bitmap
uchar* box_datas;
// 裁剪输出目录
String rootDir;
// 在该文件的目录下创建与文件名相同的目录
// 获取目录
char path_buffer[_MAX_PATH];
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];
Vector<Format>* formats;
void macth(Mat img, string&result, Vector<Format> formats) {
// 缩小2倍
	float scale = 0.5f;
	Mat mat;
	resize(img, mat, Size(img.cols * scale, img.rows * scale));

	FormatWapper fw("def", "未知");
	for (size_t i = 0; i < formats.size(); i++) {
		Format format = formats[i];
		int x = (int) (format.x * mat.cols);
		int y = (int) (format.y * mat.rows);
		int width = (int) (format.w * mat.cols);
		int height = (int) (format.h * mat.rows);
		Rect rect = seekCut(mat, Rect(x, y, width, height));
		if (rect.x <= 2 || rect.y <= 2 || rect.width <= 2 || rect.height <= 2) {
//			cout << "unexpected(seek):" << format.name << endl;
			continue;
		}

//		imshow("m", process::screen(mat, Rect(x, y, width, height)));

		Mat shard = process::screen(mat, rect);
		int width1 = format.getMaxtir().cols;
		int height1 = format.getMaxtir().rows;
		shard = process::gray(shard);
		resize(shard, shard, Size(width1, height1));
		adaptiveThreshold(shard, shard, 255, CV_ADAPTIVE_THRESH_MEAN_C,
				CV_THRESH_BINARY_INV, 21, 25);
		float result = cal(shard, format.getMaxtir());
		if (result > fw.result) {
			fw.result = result;
			fw.id = format.id;
			fw.name = format.name;
		}
//		cout << format.name << "相似度: " << result << endl;
//		imshow("maxtir", shard);
//		imshow("maxtir1", format.getMaxtir());
//		waitKey(0);
	}

	if (fw.result < 0.45) {
		fw.id = "null";
		fw.name = "null";
	}

	result.append(fw.id).append("-").append(fw.name);

}
// 分类版面
void classifier(const char* filepath, string&result) {
	Mat mat = imread(filepath);
	Mat img;
	blur(mat, img, Size(3, 3));
	Vector<Format> tmps;
	simpleFileter(img, *formats, tmps);
	macth(img, result, tmps);
}

void simpleFileter(Mat img, Vector<Format> &formats, Vector<Format> &tmps) {
	if (formats.size() == 0) {
		cout << "format is empty !" << endl;
		exit(-1);
	}
	float curScale = (float) img.cols / img.rows;
	Vector<Format>::iterator it = formats.begin();
	for (; it != formats.end(); it++) {
		Format* format = it;
		float scale = format->scale;
		if (scale - 0.15f < curScale && curScale < scale + 0.15f) {
			Format temp;
			temp.x = format->x;
			temp.y = format->y;
			temp.w = format->w;
			temp.h = format->h;
			temp.scale = format->scale;
			temp.id = format->id;
			temp.name = format->name;
			temp.scale = format->scale;
			temp.setMaxtir(format->getMaxtir());
			tmps.push_back(temp);
			continue;
		}
//		cout << "unexpected(size):" << format->name << endl;
	}
}

wstring StringToWString(const std::string &str) {
	std::wstring wstr(str.length(), L' ');
	std::copy(str.begin(), str.end(), wstr.begin());
	return wstr;
}

void fixFileName(string oldFilename, string newFilename) {
	wstring a = StringToWString(oldFilename);
	wstring b = StringToWString(newFilename);
	LPCWSTR str = a.c_str();
	LPCWSTR str1 = b.c_str();
	MoveFileW(str, str1);
}

void cutpt(Mat img, const char* pt, const char* outpath) {
	// 左下、左上、右下、右上
	Vector<string> strs = StrUtil::split(pt, ";");
	// 左上角
	Vector<string> strs1 = StrUtil::split(strs[1], ",");
	int x1 = StrUtil::strToInt(strs1[0].c_str());
	int y1 = StrUtil::strToInt(strs1[1].c_str());

	// 右上角
	Vector<string> strs2 = StrUtil::split(strs[3], ",");
	int x2 = StrUtil::strToInt(strs2[0].c_str());
	int y2 = StrUtil::strToInt(strs2[1].c_str());

	// 左下角
	Vector<string> strs3 = StrUtil::split(strs[0], ",");
	int x3 = StrUtil::strToInt(strs3[0].c_str());
	int y3 = StrUtil::strToInt(strs3[1].c_str());

	// 右下角
	Vector<string> strs4 = StrUtil::split(strs[2], ",");
	int x4 = StrUtil::strToInt(strs4[0].c_str());
	int y4 = StrUtil::strToInt(strs4[1].c_str());

	// 取最小角度
	double angle1 = atan((double) abs(y2 - y1) / abs(x1 - x2));
	double angle2 = atan((double) abs(y3 - y4) / abs(x3 - x4));
	double angle =
			angle1 > angle2 ?
					angle2 * (y3 > y4 ? 1 : -1) : angle1 * (y1 > y2 ? 1 : -1);

	angle = 180 / M_PI * angle;

	int x11 = max(0, max(max(max(x1, x2), x3), x4));
	int x22 = max(0, min(min(min(x1, x2), x3), x4));
	int y11 = max(0, max(max(max(y1, y2), y3), y4));
	int y22 = max(0, min(min(min(y1, y2), y3), y4));

	// 裁剪小于一定面积舍弃
	if(abs(x11 - x22) * abs(y11 - y22) < 150 * 150) {
		log::info("cutpt 舍弃小面积结果.");
		imwrite(outpath, img);
		return;
	}

	Rect r(x22, y22, abs(x11 - x22), abs(y11 - y22));
	Mat roateImg;
	if (abs(angle) > 0.01) {
		roateImg = process::roateImg(img, angle,
				Point(r.x + r.width / 2, r.y + r.height / 2));
		r = suoxiaoRect(roateImg, r);
	} else {
		roateImg = img;
	}

	Rect outRect;
	outRect.x = r.x + INN_PX / 2;
	outRect.y = r.y + INN_PX / 2;
	outRect.width = r.width - INN_PX;
	outRect.height = r.height - INN_PX;

	Mat mm = process::screen(roateImg, outRect);
	imwrite(outpath, mm);
}

void cutpt(uchar* m, int width, int height, int step, const char* pt, const char* outpath) {
	int model = CV_8UC3;

	if (step / width == 4) {
		model = CV_8UC4;
	}
	Mat img(height, width, model, m, step);
	if (model == CV_8UC4) {
		cvtColor(img, img, CV_RGBA2RGB);
	}
	cutpt(img, pt, outpath);
}

// 裁剪, 路径、单/多目标、测试窗口
void cut(const char* filepath, bool multiple, bool b1) {
	log::info(string(filepath).append("执行切分方法."));

	_splitpath(filepath, drive, dir, fname, ext);
	_makepath(path_buffer, drive, dir, "tmp", "");

	log::info(string("生成输出目录: .").append(path_buffer));

	mkdir(path_buffer);
// 创建的目录路径
	rootDir = path_buffer;
// 源文件绝对路径
	string src_name = path_buffer + string("\\") + string(fname) + string(ext);
	imwrite(src_name, imread(filepath));

	execute(filepath, multiple, b1, 2);

	char new_file_name[_MAX_PATH];
	_makepath(new_file_name, drive, dir, fname, "");

	fixFileName(path_buffer, new_file_name);

	log::info(string(filepath).append(" 执行切分方法完成."));
	log::info("============================================.");
	log::info("============================================.");
}

// 画框, 路径、单/多目标、测试窗口
uchar* box(const char* filepath, bool multiple, bool b1) {
	execute(filepath, multiple, b1, 1);
	return data;
}

// bitmap，长，宽，步长，单/多目标，测试窗口
uchar* boxbm(uchar*m, int width, int height, int step, bool multiple,
		bool testWin) {
	if (testWin) {
		cout << "box execute..." << endl;
	}
	int model = CV_8UC3;

	if (step / width == 4) {
		model = CV_8UC4;
	}
	Mat img(height, width, model, m, step);
	if (model == CV_8UC4) {
		cvtColor(img, img, CV_RGBA2RGB);
	}

	execute(img, multiple, testWin, 1);
	return data;
}

// bitmap，长，宽，步长，单/多目标，测试窗口
string boxpt(uchar*m, int width, int height, int step, bool multiple,
		bool testWin) {
	int model = CV_8UC3;

	if (step / width == 4) {
		model = CV_8UC4;
	}
	Mat img(height, width, model, m, step);
	if (model == CV_8UC4) {
		cvtColor(img, img, CV_RGBA2RGB);
	}

	execute(img, multiple, testWin, 1);
	return str_pt;
}

void execute(Mat src, bool multiple, bool testWin, int model) {
	if (src.size().height == 0 || src.size().width == 0) {
		log::info("读取图片失败.");
		exit(0);
	}
	Rect rSrc;

	rSrc.x = 30.0 / 320.0 * src.cols;
	rSrc.y = 1;
	rSrc.width = 270.0 / 320.0 * src.cols;
	rSrc.height = 220.0 / 250.0 * src.rows;

	log::info(
			string("预处理裁剪区域 # x:").append(StrUtil::intToStr(rSrc.x)).append(
					",y:").append(StrUtil::intToStr(rSrc.y)).append(",w:").append(
					StrUtil::intToStr(rSrc.width)).append(",h:").append(
					StrUtil::intToStr(rSrc.height)));

	log::info("开始对源图片预处理.");
	Mat img = process::screen(src, rSrc);
// 灰度
	Mat gray = process::gray(img);
// 缩放
	Mat resize = process::gray(
			process::resize(img, Size(img.cols / 4, img.rows / 4)));
	log::info("预处理源图片完成.");

	log::info("开始寻找目标区域.");
// 寻找目标矩形区域
	Vector<Rect> rects = find(resize, multiple);
	rects = filterRectsSize(rects);
// 再一次进行过滤处理，主要解决边缘区域是否和识别内容有重合
	rects = filterRects(resize, rects);
	log::info("过滤目标区域.");
	if (test) {
		Mat mt;
		resize.copyTo(mt);
		for (size_t i = 0; i < rects.size(); i++) {
			rectangle(mt, rects[i], Scalar(255));
		}
		imshow("seek", mt);
	}

	if (rects.size() == 0) {
		log::info(string("寻找目标区域失败"));
		str_pt = "0,0;0,0;0,0;0,0";
		return;
	}

	log::info(
			string("寻找目标区域完成, 目前找到目标区域数量为: .").append(
					StrUtil::intToStr(rects.size())).append(", 详细位置.: x:").append(
					StrUtil::intToStr(rects[0].x * 4)).append(",y:").append(
					StrUtil::intToStr(rects[0].y * 4)).append(",w:").append(
					StrUtil::intToStr(rects[0].width * 4)).append(",h:").append(
					StrUtil::intToStr(rects[0].height * 4)));
	log::info("开始纠偏目标区域.");
// 保存旋转矩阵
	Vector<RotatedRect> rotatedRects;
// 保存裁剪的图片
	Vector<Mat> results;
	for (size_t i = 0; i < rects.size(); i++) {
		int length = 10;
		Rect rect = rects[i];
		Rect tmp;
		tmp.x = rect.x - length;
		tmp.y = rect.y - length;
		tmp.width = rect.width + length * 2;
		tmp.height = rect.height + length * 2;

		if (tmp.x < 0) {
			tmp.x = 0;
		}

		if (tmp.y < 0) {
			tmp.y = 0;
		}

		if (tmp.y + tmp.height >= resize.rows) {
			tmp.height = resize.rows - tmp.y - 1;
		}

		if (tmp.x + tmp.width >= resize.cols) {
			tmp.width = resize.cols - tmp.x - 1;
		}

		length =
				abs(tmp.x - rect.x) <= abs(tmp.y - rect.y) ?
						abs(tmp.x - rect.x) : abs(tmp.y - rect.y);
		int length1 =
				abs(resize.cols - tmp.x - tmp.width)
						<= abs(resize.rows - tmp.y - tmp.height) ?
						abs(resize.cols - tmp.x - tmp.width) :
						abs(resize.rows - tmp.y - tmp.height);
		length = length <= length1 ? length : length1;
		service::dregee = process::calDegree(process::screen(resize, tmp),
				length);

		stringstream ss;
		ss << service::dregee;
		log::info(string("纠偏成功,  纠偏角度为: ").append(ss.str()));

		if (test) {
			Mat m = process::screen(resize, tmp);
			blur(m, m, Size(5, 5));
			Mat shardPz = process::pz(m, Size(10, 10));
			shardPz = process::fs(shardPz, Size(10, 10));
			imshow("cal degree pz", shardPz);

			// 边缘
			Mat canny;
			Canny(shardPz, canny, 100, 150);
			canny.rowRange(0, length).setTo(Scalar(0));
			canny.rowRange(canny.rows - 1 - length, canny.rows - 1).setTo(
					Scalar(0));
			canny.colRange(0, length).setTo(Scalar(0));
			canny.colRange(canny.cols - 1 - length, canny.cols - 1).setTo(
					Scalar(0));
			// 去除干扰
			vector<vector<Point> > contours;
			vector<Vec4i> hierarchy;
			// 寻找轮廓
			findContours(canny.clone(), contours, hierarchy, CV_RETR_TREE,
					CV_CHAIN_APPROX_SIMPLE);
			// 对每个轮廓计算其凸包
			vector<vector<Point> > hull(contours.size());
			for (size_t i = 0; i < contours.size(); i++) {
				convexHull(Mat(contours[i]), hull[i], false);
			}
			Vector<Rect> rects;
			// 绘出轮廓及其凸包
			for (size_t i = 0; i < hull.size(); i++) {
				Rect rect = boundingRect(Mat(hull[i]));
				if (rect.height * rect.width < 100) {
					rectangle(canny, rect, Scalar(0), -1);
				}
			}
			imshow("jpyj", canny);
		}

		log::info("开始根据角度裁剪原图目标区域.");

		Rect r;
		r.x = rect.x * 4 + rSrc.x;
		r.y = rect.y * 4 + rSrc.y;
		r.width = rect.width * 4;
		r.height = rect.height * 4;
		// 原图片的裁剪区域
		Mat roateImg = process::roateImg(src, service::dregee,
				Point(r.x + r.width / 2, r.y + r.height / 2));
		// 旋转后的效果图
		if (test) {
			imshow("xuanzhuangImg", roateImg);
		}
		Mat resize4RoateImg = process::resize(roateImg,
				Size(src.cols, src.rows));
		// 得出准确的矩阵位置
		r = suoxiaoRect(resize4RoateImg, r);
		log::info(
				string("根据纠偏角度得出准确的目标区域详细信息: x:.").append(
						StrUtil::intToStr(rects[0].x * 4)).append(",y:").append(
						StrUtil::intToStr(rects[0].y * 4)).append(",w:").append(
						StrUtil::intToStr(rects[0].width * 4)).append(",h:").append(
						StrUtil::intToStr(rects[0].height * 4)));
		// 裁剪
		if (model == 2) {
			log::info("开始裁剪.");

			Rect outRect;
			outRect.x = r.x + INN_PX / 2;
			outRect.y = r.y + INN_PX / 2;
			outRect.width = r.width - INN_PX;
			outRect.height = r.height - INN_PX;

			Mat result = process::screen(roateImg, outRect);
			results.push_back(result);
			log::info(
					string("裁剪完成, 裁剪图片大小为.").append(
							StrUtil::intToStr(result.cols)).append("*").append(
							StrUtil::intToStr(resize.rows)));
			// 显示裁剪的结果
			if (test) {
				stringstream ss;
				ss << i;
				imshow(string("cutresult") + ss.str(), result);
			}
		}
		// 画框
		if (model == 1) {
			if(r.width * r.height < 150 * 150) {
				log::info("boxpt 舍弃小面积结果.");
			}
			else {
				log::info("开始画框.");
				rotatedRects.push_back(
						RotatedRect(Point2f(r.x + r.width / 2, r.y + r.height / 2),
								Size2f(r.width, r.height), service::dregee * -1));
				log::info("保存画框信息.");
			}
		}
		if (test) {
			waitKey(0);
		}
	}
// 保存裁剪图片
	if (results.size() != 0) {
		log::info("开始裁剪图片.");
		for (size_t i = 0; i < results.size(); i++) {
			stringstream ss;
			ss << i;
			// 保存裁剪结果
			string shard_name = path_buffer + string("\\") + ss.str()
					+ string("_shard") + string(ext);
			imwrite(shard_name, results[i]);
		}
		log::info("保存裁剪结果完成.");
	}
// 画出矩形
	str_pt.clear();
	if (rotatedRects.size() != 0) {
		log::info("输出目标区域四个点的坐标.");
		IplImage ipl_img(src);
		service::uu = cvCloneImage(&ipl_img);
		for (size_t i = 0; i < rotatedRects.size(); i++) {
			Point2f vertices[4];
			rotatedRects[i].points(vertices); //提取旋转矩形的四个角点
			for (int j = 0; j < 4; j++) {
				cvLine(service::uu, cvPoint(vertices[j].x, vertices[j].y),
						cvPoint(vertices[(j + 1) % 4].x,
								vertices[(j + 1) % 4].y), Scalar(0, 255, 0), 3);
			}
			// 左下角
			str_pt = StrUtil::intToStr((int) vertices[0].x);
			str_pt = str_pt + ",";
			str_pt = str_pt + StrUtil::intToStr((int) vertices[0].y);
			str_pt = str_pt + ";";
			// 左上角
			str_pt = str_pt + StrUtil::intToStr((int) vertices[1].x);
			str_pt = str_pt + ",";
			str_pt = str_pt + StrUtil::intToStr((int) vertices[1].y);
			str_pt = str_pt + ";";
			// 右下角
			str_pt = str_pt + StrUtil::intToStr((int) vertices[3].x);
			str_pt = str_pt + ",";
			str_pt = str_pt + StrUtil::intToStr((int) vertices[3].y);
			str_pt = str_pt + ";";
			// 右上角
			str_pt = str_pt + StrUtil::intToStr((int) vertices[2].x);
			str_pt = str_pt + ",";
			str_pt = str_pt + StrUtil::intToStr((int) vertices[2].y);
			log::info(string("得出目标区域4个点坐标为.:").append(str_pt));
		}
		Mat ss = cvarrToMat(service::uu);
		service::data = ss.data;
		if (test) {
			service::box_width = ss.size().width;
			service::box_height = ss.size().height;
			cvShowImage("rectMat", service::uu);
			cvWaitKey();
		}
	} else {
		str_pt = "0,0;0,0;0,0;0,0";
	}

	if (test) {
		cout << "complate..." << endl;
		waitKey(0);
	}
}

// 路径、单/多目标、测试窗口、1.画框 2.裁剪
void execute(const char* filepath, bool b, bool b1, int model) {
	test = b1;
	Mat image = imread(filepath);

	log::info(string("读取图片. ").append(filepath).append(" 成功."));

	execute(image, b, b1, model);
}
// b: false 单个目标，b: true 多个目标
Vector<Rect> find(Mat mat, bool b) {
	Vector<Rect> rects;
// 寻找轮廓
	Vector<Rect> tmps = process::findContoursRect(mat.clone());
// 按比例舍弃区域
	for (size_t i = 0; i < tmps.size(); i++) {
		Rect rect = tmps[i];
		bool flag = false;
		int curArea = getRectArea(rect);
		for (size_t j = 0; j < rects.size(); j++) {
			// 当前添加的区域是否重复
			if (curArea == getRectArea(rects[j])) {
				flag = true;
				break;
			}
		}
		if (flag)
			continue;
		if (rect.width > mat.cols * 0.1 && rect.height > mat.rows * 0.1) {
			rects.push_back(rect);
		}
	}
	if (rects.size() == 0) {
		return rects;
	}
// 单个目标的情况!
	if (!b) {
		if (test) {
			Mat m = mat.clone();
			// 膨胀
			Mat pz = process::pz(m, Size(10, 10));
			// 边缘
			Mat canny;
			Canny(pz, canny, 100, 150);
			imshow("canny", canny);
			imshow("pz", pz);
			waitKey(0);
		}
		Rect centerRect = findCenterRect(mat, rects);
		rects.clear();
		rects.push_back(centerRect);
		return rects;
	}
// 多个目标的情况
	Vector<int> indexs;
	for (size_t i = 0; i < rects.size(); i++) {
		Rect rect = rects[i];
		int curArea = getRectArea(rect);
		for (size_t j = 0; j < rects.size(); j++) {
			if (j == i) {
				continue;
			}
			// 判断第i个rect是否重合rect,也就是i是否合理
			// 当前区域是否有重合的区域
			// 重合条件
			if (curArea > getRectArea(rects[j]) && rect.x <= rects[j].x
					&& rect.y <= rects[j].y
					&& rect.x + rect.width >= rects[j].x + rects[j].width
					&& rect.y + rect.height >= rects[j].y + rects[j].height) {
				double scale = (double) curArea / getRectArea(rects[j]);
				// 2个区域比例差距较大
				if (scale < 0.2) {
					if (getRectArea(rects[j]) >= getRectArea(rects[i])) {
						indexs.push_back(i);
					} else {
						indexs.push_back(j);
					}
					break;
				}
				if (scale > 0.8) {
					if (getRectArea(rects[j]) >= getRectArea(rects[i])) {
						indexs.push_back(i);
					} else {
						indexs.push_back(j);
					}
					break;
				}
			}
		}
	}
	if (test) {
		Mat m = mat.clone();
		for (size_t i = 0; i < tmps.size(); i++) {
			rectangle(m, tmps[i], Scalar(255));
		}
		imshow("rectangle", m);
		// 膨胀
		Mat pz = process::pz(m, Size(10, 10));
		// 边缘
		Mat canny;
		Canny(pz, canny, 100, 150);
		imshow("canny", canny);
		imshow("pz", pz);
	}
// 如果有标记失败的矩形
	if (indexs.size() != 0) {
		Vector<Rect> vectors;
		for (size_t i = 0; i < rects.size(); i++) {
			bool flag = true;
			for (size_t j = 0; j < indexs.size(); j++) {
				if (i == (size_t) indexs[j]) {
					flag = false;
					break;

				}
			}
			if (flag) {
				vectors.push_back(rects[i]);
			}
		}
		return vectors;
	}
	return rects;
}

Rect findCenterRect(Mat mat, Vector<Rect> rects) {
// 中心点
	Point matCenter(mat.cols / 2, mat.rows / 2);
	double distanceMAX = mat.cols + mat.rows;
	int rectIndex = -1;

	for (size_t i = 0; i < rects.size(); i++) {
		Rect rect = rects[i];
		Point rectCenter(rect.x + rect.width / 2, rect.y + rect.height / 2);

		double distance = pow(
				(matCenter.x - rectCenter.x) * (matCenter.x - rectCenter.x)
						+ (matCenter.y - rectCenter.y)
								* (matCenter.y - rectCenter.y), 0.5);
		if (distance < distanceMAX) {
			distanceMAX = distance;
			rectIndex = i;
		}
	}
	if (rectIndex >= 0) {
		Rect rect;
		rect.x = rects[rectIndex].x - 2 < 0 ? 0 : rects[rectIndex].x - 2;
		rect.y = rects[rectIndex].y - 2 < 0 ? 0 : rects[rectIndex].y - 2;
		rect.width =
				rect.x + rects[rectIndex].width + 4 > mat.cols - 1 ?
						mat.cols - 1 - rect.x : rects[rectIndex].width + 4;
		rect.height =
				rect.y + rects[rectIndex].height + 4 > mat.rows - 1 ?
						mat.rows - 1 - rect.y : rects[rectIndex].height + 4;
		return rect;
	}
	Rect rect;
	rect.x = 0;
	rect.y = 0;
	rect.width = mat.cols - 1;
	rect.height = mat.rows - 1;
	return rect;
}

Rect findMaxRect(Mat mat, Vector<Rect> rects) {
	if (rects.size() == 0) {
		Rect tmp;
		tmp.x = 0;
		tmp.y = 0;
		tmp.width = mat.cols - 1;
		tmp.height = mat.rows - 1;
		rects.push_back(tmp);
	}
	if (rects.size() == 1) {
		return rects[0];
	}

	int index = 0;
	for (size_t i = 1; i < rects.size(); i++) {
		Rect rect1 = rects[index];
		Rect rect2 = rects[i];
		if (getRectArea(rect1) < getRectArea(rect2)) {
			index = i;
		}
	}

	return rects[index];
}

Rect suoxiaoRect(Mat mat, Rect r) {
	if (mat.cols < 4 || mat.rows < 4) {
		return r;
	}
	float scale = 0.5;
	Mat shard = process::screen(mat, r);
	shard = process::resize(shard,
			Size(shard.cols * scale, shard.rows * scale));
	Mat shardPz = process::gray(shard);
	shardPz = process::pz(shardPz, Size(10, 10));
// 边缘
	Mat canny;
	Canny(shardPz, canny, 100, 150);
// 去除干扰
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;
// 寻找轮廓
	findContours(canny.clone(), contours, hierarchy, CV_RETR_TREE,
			CV_CHAIN_APPROX_SIMPLE);
// 对每个轮廓计算其凸包
	vector<vector<Point> > hull(contours.size());
	for (size_t i = 0; i < contours.size(); i++) {
		convexHull(Mat(contours[i]), hull[i], false);
	}
// 绘出轮廓及其凸包
	for (size_t i = 0; i < hull.size(); i++) {
		Rect rect = boundingRect(Mat(hull[i]));
		if (rect.height * rect.width < 100) {
			rectangle(canny, rect, Scalar(0), -1);
		}
	}
	if (test) {
		imshow("suoxiaorectyj", canny);
	}
	// 行号,白点的数量
	map<int, int> m;
	map<int, int>::iterator it;
	int top = 0;
	for (int i = canny.cols / 4; i < canny.cols * 3 / 4; i++) {
		for (int row = 0; row < (canny.rows - 1) / 2; row++) {
			uchar* p1 = canny.ptr<uchar>(row);
			int pex = (int) *(p1 + i);
			// 白色
			if (pex > 100) {
				m[row] += 1;
				break;
			}
		}
	}
	it = m.begin();
	while (it != m.end()) {
		if (m[top] < it->second) {
			top = it->first;
		}
		it++;
	}

	m.clear();
	int down = canny.rows - 1;
	for (int i = canny.cols / 4; i < canny.cols * 3 / 4; i++) {
		for (int row = canny.rows - 1; row > canny.rows / 2; row--) {
			uchar* p1 = canny.ptr<uchar>(row);
			int pex = (int) *(p1 + i);
			// 白色
			if (pex > 100) {
				m[row] += 1;
				break;
			}
		}
	}

	it = m.begin();
	while (it != m.end()) {
		if (m[down] < it->second) {
			down = it->first;
		}
		it++;
	}

	m.clear();
	int left = 0;
	for (int i = canny.rows / 4; i < canny.rows * 3 / 4; i++) {
		uchar* p = canny.ptr<uchar>(i);
		for (int col = 0; col < (canny.cols - 1) / 2; col++) {
			int pex = (int) *(p + col);
			// 白色
			if (pex > 100) {
				m[col] += 1;
				break;
			}
		}
	}

	it = m.begin();
	while (it != m.end()) {
		if (m[left] < it->second) {
			left = it->first;
		}
		it++;
	}

	m.clear();
	int right = canny.cols - 1;
	for (int i = canny.rows / 4; i < canny.rows * 3 / 4; i++) {
		uchar* p = canny.ptr<uchar>(i);
		for (int col = canny.cols - 1; col > canny.cols / 2; col--) {
			int pex = (int) *(p + col);
			// 白色
			if (pex > 100) {
				m[col] += 1;
				break;
			}
		}
	}
	it = m.begin();
	while (it != m.end()) {
		if (m[right] < it->second) {
			right = it->first;
		}
		it++;
	}
	m.clear();
	left /= scale;
	right /= scale;
	top /= scale;
	down /= scale;
	if (test) {
		cout << "left:" << left << ",right:" << right << ",top:" << top
				<< ",down:" << down << endl;
	}
	if (left < right) {
		r.x = r.x + left;
		r.width = right - left;
	}
	if (top < down) {
		r.y = r.y + top;
		r.height = mat.rows - (mat.rows - down + top);
	}
	return r;
}

bool check(Vec3b pex1, Vec3b pex2) {
	int index = 0;

	if (abs(pex1[2] - pex2[2]) > 40) {
		index++;
	}
	if (abs(pex1[1] - pex2[1]) > 40) {
		index++;
	}
	if (abs(pex1[0] - pex2[0]) > 40) {
		index++;
	}
	return index >= 2 ? true : false;
}

int getRectArea(Rect rect) {
	return rect.width * rect.height;
}

Vector<Rect> filterRectsSize(Vector<Rect> rects) {
	Vector<Rect> vectors;
	for (size_t i = 0; i < rects.size(); i++) {
		double bili = (double) rects[i].width / (double) rects[i].height;
		if (bili < 0.2) {
			continue;
		}
		bili = (double) rects[i].height / (double) rects[i].width;
		if (bili < 0.2) {
			continue;
		}
		vectors.push_back(rects[i]);
	}

	return vectors;
}

Vector<Rect> filterRects(Mat mat, Vector<Rect> rects) {
	Vector<Rect> vectors;
	Vector<int> removes;
	int MAX = 10;
	for (size_t i = 0; i < rects.size(); i++) {
		Rect rect = rects[i];
		//检测单签rect 是否贴边 。
		bool flag = false;
		if (rect.x < MAX || rect.y < MAX || rect.x + rect.width > mat.cols - MAX
				|| rect.y + rect.height > mat.rows - MAX) {
			for (size_t j = 0; j < rects.size(); j++) {
				if (i == j) {
					continue;
				}
				// 检查是否和其他的rect重合
				if (check1(rect, Point(rects[j].x, rects[j].y))
						|| check1(rect,
								Point(rects[j].x + rects[j].width, rects[j].y))
						|| check1(rect,
								Point(rects[j].x, rects[j].y + rects[j].height))
						|| check1(rect,
								Point(rects[j].x + rects[j].width,
										rects[j].y + rects[j].height))) {
					flag = true;
					break;
				}
			}
		}
		// 没有重合
		if (!flag) {
			bool b = true;
			for (size_t j = 0; j < removes.size(); j++) {
				if ((size_t) removes[j] == i) {
					b = false;
					break;
				}
			}
			if (b)
				vectors.push_back(rects[i]);
		}

		if (flag) {
			removes.push_back(i);
		}
	}
	return vectors;
}

bool check1(Rect rect, Point point) {
	int x = point.x;
	int y = point.y;
	if (rect.x < x && rect.y < y && rect.x + rect.width > x
			&& rect.y + rect.height > y) {
		return true;
	}
	return false;
}

void loadFormatData(const char* filepath) {
	string path = DataUtil::getCurDir() + "/data/format";
	Vector<string> files;
	Vector<string> names;
	DataUtil::getFiles(path, files, names);
	formats = new Vector<Format>();
	for (size_t i = 0; i < files.size(); i++) {
		map<string, string> map;
		ReadConfig(files[i], map);
		Format format;
		format.x = stringToNum<double>(map["x"]);
		format.y = stringToNum<double>(map["y"]);
		format.w = stringToNum<double>(map["w"]);
		format.h = stringToNum<double>(map["h"]);
		format.scale = stringToNum<double>(map["scale"]);
		format.formatData = map["formatData"];
		Vector<string> datas = StrUtil::split(format.formatData, ",");
		int width = StrUtil::strToInt(datas[0].c_str());
		int height = StrUtil::strToInt(datas[1].c_str());
		int start = 2;
		if (width * height == datas.size() - start) {
			Mat maxtir = Mat(Size(width, height), CV_8UC1);
			for (int row = 0; row < height; row++) {
				uchar* pxvec = maxtir.ptr<uchar>(row);
				for (int col = 0; col < width; col++) {
					pxvec[col] = StrUtil::strToInt(datas[start++].c_str());
				}
			}
			format.setMaxtir(maxtir);
//			imshow("1", maxtir);
//			waitKey(0);
		} else {
			cout << map["name"] << "format data load fail.!" << endl;
			continue;
		}
		format.name = map["name"];
		format.id = map["id"];
		formats->push_back(format);
	}
	cout << "load format path.: " << path << endl;
}

template<class Type>
Type stringToNum(const string& str) {
	istringstream iss(str);
	Type num;
	iss >> num;
	return num;
}

Rect seekCut(Mat mat, Rect rect) {
	Mat gary = process::gray(mat);
	Mat shard = process::screen(gary, rect);
	adaptiveThreshold(shard, shard, 255, CV_ADAPTIVE_THRESH_MEAN_C,
			CV_THRESH_BINARY_INV, 21, 25);
	shard.row(0).setTo(Scalar(0));
	shard.row(shard.rows - 1).setTo(Scalar(0));
	shard.col(0).setTo(Scalar(0));
	shard.col(shard.cols - 1).setTo(Scalar(0));
	Vector<int> rows;
	getRows(shard, rows);
	int start = 0;
	int end = shard.rows - 1;
	if (rows.size() == 3 || rows.size() == 2) {
		start = rows[0];
		end = rows[1];
	} else if (rows.size() > 3) {
		int maxRow = 0;
		for (size_t i = 1; i < rows.size(); i += 2) {
			int poor = rows[i] - rows[i - 1];
			if (maxRow < poor) {
				start = rows[i - 1];
				end = rows[i];
				maxRow = poor;
			}
		}
		shard.rowRange(0, start).setTo(Scalar(0));
		shard.rowRange(end, shard.rows - 1).setTo(Scalar(0));
	}
	Vector<int> cols;
	getCols(shard, cols);

	int left = 0;
	int right = 1;
	if (cols.size() == 3 || cols.size() == 2) {
		left = cols[0];
		right = cols[1];
	} else if (cols.size() > 3) {
		for (size_t i = 1; i < cols.size(); i += 2) {
			int poor = cols[i] - cols[i - 1];
			if (poor > 2) {
				left = cols[i - 1];
				break;
			}
		}
		for (size_t i = cols.size() - 1; i >= 0; i -= 2) {
			int poor = cols[i] - cols[i - 1];
			if (poor > 2) {
				right = cols[i];
				break;
			}
		}
	}
	rect.x = rect.x + left;
	rect.y = rect.y + start;
	rect.width = abs(left - right);
	rect.height = abs(start - end);
	return rect;
}

void getRows(Mat shard, Vector<int> &rows) {
	int preRowColor = 1;
	int curRowColor = 1;
	// 获得切分行号
	for (int row = 0; row < shard.rows; row++) {
		uchar* pxvec = shard.ptr<uchar>(row);
		curRowColor = 1;
		for (int col = shard.cols * 0.05; col < shard.cols * 0.95; col++) {
			int pex = (int) *(pxvec + col);
			// 像素为白色
			if (pex > 150) {
				curRowColor = 0;
			}
		}
		if (curRowColor != preRowColor) {
			rows.push_back(row);
		}
		preRowColor = curRowColor;
	}
}

void getCols(Mat shard, Vector<int>& cols) {
	int preColColor = 1;
	int curColColor = 1;
	int start = 0;
	int end = shard.rows - 1;

	for (int col = 0; col < shard.cols; col++) {
		curColColor = 1;
		for (int row = start; row < end; row++) {
			uchar* pxvec = shard.ptr<uchar>(row);
			int pex = (int) *(pxvec + col);
			if (pex > 100) {
				curColColor = 0;
			}
		}

		if (curColColor != preColColor) {
			if (col != 0 && col + 1 != cols.size()) {
				cols.push_back(col);
			}
		}
		preColColor = curColColor;
	}
}

float cal(Mat mat1, Mat mat2) {
	int len = mat1.rows * mat1.cols;
	float *matData1 = new float[len];
	int j = 0;
	for (int n = 0; n < mat1.rows; n++) {
		uchar* pxvec = mat1.ptr<uchar>(n);
		for (int m = 0; m < mat1.cols; m++) {
			matData1[j++] = (int) *(pxvec++);
		}
	}
	float *matData2 = new float[len];
	j = 0;
	for (int n = 0; n < mat2.rows; n++) {
		uchar* pxvec = mat2.ptr<uchar>(n);
		for (int m = 0; m < mat2.cols; m++) {
			matData2[j++] = (int) *(pxvec++);
		}
	}

	float dot = 0;
	for (int i = 0; i < len; i++) {
		dot += matData1[i] * matData2[i];
	}

	float a1 = 0;
	for (int i = 0; i < len; i++) {
		a1 += matData1[i] * matData1[i];
	}
	a1 = sqrt(a1);

	float a2 = 0;
	for (int i = 0; i < len; i++) {
		a2 += matData2[i] * matData2[i];
	}
	a2 = sqrt(a2);

	if (a1 * a2 == 0) {
		return 0;
	}

	float result = dot / (a1 * a2);
	return result;
}
}

#endif /* HPP_SERVICE_HPP_ */
